{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b84c6035-ca65-46fd-b365-f733bbc83a85",
   "metadata": {},
   "source": [
    "# 延后初始化\n",
    "\n",
    "到目前为止，我们忽略了建立网络时需要做的以下这些事情：\n",
    "\n",
    "* 我们定义了网络架构，但没有指定输入维度。\n",
    "* 我们添加层时没有指定前一层的输出维度。\n",
    "* 我们在初始化参数时，甚至没有足够的信息来确定模型应该包含多少参数。\n",
    "\n",
    "有些读者可能会对我们的代码能运行感到惊讶。\n",
    "毕竟，深度学习框架无法判断网络的输入维度是什么。\n",
    "这里的诀窍是框架的*延后初始化*（defers initialization），\n",
    "即直到数据第一次通过模型传递时，框架才会动态地推断出每个层的大小。\n",
    "\n",
    "在以后，当使用卷积神经网络时，\n",
    "由于输入维度（即图像的分辨率）将影响每个后续层的维数，\n",
    "有了该技术将更加方便。\n",
    "现在我们在编写代码时无须知道维度是什么就可以设置参数，\n",
    "这种能力可以大大简化定义和修改模型的任务。\n",
    "接下来，我们将更深入地研究初始化机制。\n",
    "\n",
    "## 实例化网络\n",
    "\n",
    "首先，让我们实例化一个多层感知机。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f27ec42e-62a5-4fd4-9ea3-1a2d2ef0558d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "\n",
    "net = tf.keras.models.Sequential([\n",
    "    tf.keras.layers.Dense(256, activation=tf.nn.relu),\n",
    "    tf.keras.layers.Dense(10),\n",
    "])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2bb6dba8-09d6-40ab-9944-59c22f242d3a",
   "metadata": {},
   "source": [
    "此时，因为输入维数是未知的，所以网络不可能知道输入层权重的维数。\n",
    "因此，框架尚未初始化任何参数，我们通过尝试访问以下参数进行确认。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "94dd7d6d-ce14-4068-bbc6-3a35ea1b6c73",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[], []]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[net.layers[i].get_weights() for i in range(len(net.layers))]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac2ec12e-a3d5-42a9-bf1c-89d46e431a83",
   "metadata": {},
   "source": [
    "请注意，每个层对象都存在，但权重为空。\n",
    "使用`net.get_weights()`将抛出一个错误，因为权重尚未初始化。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "980185f6-9e72-4ccb-b281-41cfe2a18ceb",
   "metadata": {},
   "source": [
    "接下来让我们将数据通过网络，最终使框架初始化参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "e4304fe9-f264-4c43-8ef0-698030bb557e",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(20, 256), (256,), (256, 10), (10,)]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X = tf.random.uniform((2, 20))\n",
    "net(X)\n",
    "[w.shape for w in net.get_weights()]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a8191f9-b66d-404a-aad2-050ca9a5ef9f",
   "metadata": {},
   "source": [
    "一旦我们知道输入维数是20，框架可以通过代入值20来识别第一层权重矩阵的形状。 识别出第一层的形状后，框架处理第二层，依此类推，直到所有形状都已知为止。 注意，在这种情况下，只有第一层需要延迟初始化，但是框架仍是按顺序初始化的。 等到知道了所有的参数形状，框架就可以初始化参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c092e7dc-e328-477a-8b53-cc5b9f0b9c97",
   "metadata": {},
   "source": [
    "## 小结\n",
    "\n",
    "* 延后初始化使框架能够自动推断参数形状，使修改模型架构变得容易，避免了一些常见的错误。\n",
    "* 我们可以通过模型传递数据，使框架最终初始化参数。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
